

# Abstract class for the conversion of the RESTful API input
# Since the data layout between json and atom is completely
# different (since the structure in atom has a atom meaning
# and a social data meaning), we have the need to put the
# hoisting rules somewhere..

class InputConverter :

	def convertPeople( self, requestParam):
		from lib import Util;
		Util.abstract();

	def convertActivities( self, requestParam):
		from lib import Util;
		Util.abstract();

	def convertAppData( self, requestParam):
		from lib import Util;
		Util.abstract();

	def convertMessages( self, requestParam):
		from lib import Util;
		Util.abstract();


# Convert json representations to the internal data structure representation

class InputJsonConverter(InputConverter):

	def convertPeople(self, requestParam):
		raise Exception, "Opperation not supported";
	

	def convertActivities(self, requestParam):
		from lib import Util;
		ret = Util.json_decode(requestParam, True);
		if (ret == requestParam):
			raise Exception, "Mallformed activity json string";
		
		return ret;
	

	def convertAppData(self, requestParam):
		from lib import Util;
		ret = Util.json_decode(requestParam, True);
		if (ret == requestParam):
			raise Exception, "Mallformed app data json string";
		
		return ret;
	

	def convertJsonBatch(self, requestParam):
		from lib import Util;
		ret = Util.json_decode(requestParam, True);
		if (ret == requestParam):
			raise Exception, "Mallformed json batch string";
		
		return ret;
	

	def convertMessages(self, requestParam):
		from lib import Util;
		ret = Util.json_decode(requestParam, True);
		if (ret == requestParam):
			raise Exception, "Mallformed message string";
		
		return ret;

# Abstract class for the Output conversion of the RESTful API
class OutputConverter:

	def __init__(self):
		self.__boundry_ = '';

	def outputResponse(self, responseItem, requestItem):
		from lib import Util;
		Util.abstract();

	def outputBatch(self, responses, token):
		from lib import Util;
		Util.abstract();



# Format = json output converter

class OutputJsonConverter(OutputConverter):

	def __init__(self, request, response):
		self.request = request;
		self.response = response;

	def outputResponse(self, responseItem,requestItem):
		response = responseItem.getResponse();
		if (isinstance(response, RestfulCollection)):
			itemsPerPage = requestItem.getCount();
			if (itemsPerPage > 0):
				response.itemsPerPage = itemsPerPage;
		
		#echo self::json_format(json_encode(response)); // TODO: add a query option to pretty-print json output
		from lib import Util;
		Util.write(Util.json_encode(response));
	

	def outputBatch(self, responses, token):
		this.boundryHeaders();
		for response in responses:
			request = response['request'];
			response = response['response'];
			from lib import Util;
			part = Util.json_encode(response);
			this.outputPart(part, response.getError());
		
	def outputJsonBatch(self, responses, token):
		from lib import Util;
		response = Util.json_encode({"responses" : responses, "error" : False});
		self.response.out.write(response);
	


	 # Generate a pretty-printed representation of a JSON object.
	 # 
	 # Taken from php comments for json_encode.

	 # @param string json  JSON string
	 # @return string|false The pretty version, false if JSON was invalid

	@staticmethod

	def json_format(json):
		tab = "  ";
		new_json = "";
		indent_level = 0;
		in_string = False;
		
		from lib import Util;
		json_obj = Util.json_decode(json);
		
		if (json_obj == None):
			return False;
		
		json = Util.json_encode(json_obj);
		len = len(json);
		
		for c in range(0, len):
			char = json[c];
			if (char == '{' or char == '['):
				if (not in_string):
					new_json = new_json + char + "\n" + str_repeat(tab, indent_level + 1);
					indent_level = indent_level + 1;
				else:
					new_json = new_json + char;

			elif (char == '}' or char == ']'):					
				if (not in_string):
					indent_level = indent_level - 1;
					new_json = new_json  + "\n" + str_repeat(tab, indent_level) + char;
				else:
					new_json = new_json  + char;
					
			elif (char == ','):
			
				if (not in_string):
					new_json = new_json  + ",\n" + str_repeat(tab, indent_level);
				else:
					new_json = new_json + char;
				
			elif (char == ':'):				
				if (not in_string):
					new_json = new_json + ": ";
				else:
					new_json = new_json + char;
					
			elif (char == '"'):				
				in_string = not in_string;
				
			else:
				new_json = new_json + char;
			

		return new_json;
	

# Convert Atom representations to the internal data structure representation

class InputAtomConverter(InputConverter):

	def convertPeople(self, requestParam):
		raise Exception, "Operation not supported";
	

	def convertActivities(self, requestParam):
		activity = {};
		from xml.etree import SimpleXMLTreeBuilder;
		parser = SimpleXMLTreeBuilder.TreeBuilder();
		parser.feed(requestParam);
		xml = parser.close();
		
		if (xml.find('title') == None):
			raise Exception, "Mallformed activity xml";
		
		#  remember to either type cast to (string) or trim() the string so we don't get 
		#  SimpleXMLString types in the internal data representation. I often prefer
		#  using trim() since it cleans up the data too
		arrays = {'id': 'id', 'title': 'title', 'body': 'summary', 'updated': 'updated',
			'streamTitle': 'content/activity/streamTitle', 'streamId': 'content/activity/streamId'	
		
			};

		for key in arrays.keys():
			value = arrays[key];
			if(xml.find(value) != None):
				activity[key] = xml.find(value).text.strip();
			else:
				activity[key] = '';

		if (xml.findall('content/activity/mediaItems') != None):
			activity['mediaItems'] = {};
			for mediaItem in xml.findall('content/activity/mediaItems/MediaItem'):
				item = {};
				if (mediaItem.find('type') == None or mediaItem.find('mimeType') == None or mediaItem.find('url')):
					raise Exception, "Invalid media item in activity xml";
				
				item['type'] = xml.find('type').text.strip();
				item['mimeType'] = xml.find('mimeType').text.strip();
				item['url'] = xml.find('url').text.strip();
				activity['mediaItems'].append(item);
			
		return activity;
	

	def convertAppData(self, requestParam):
		from xml.etree import SimpleXMLTreeBuilder;
		parser = SimpleXMLTreeBuilder.TreeBuilder();
		parser.feed(requestParam);
		xml = parser.close();

		if (xml.find('content') == None or xml.find('content').find('appdata') == None):
			raise Exception, "Mallformed AppData xml";
		
		data = {};
		
		#from lib import Logger;
		#Logger.quickLog("<b>content/appdata code need to be fixed</b>", Logger.INFO);

		#foreach (get_object_vars(xml.content.appdata) as key => val) 
		#	data[trim(key)] = trim(val);
		for key in xml.findall('content/appdata'):
			val = '';#key[];
			data[key.strip()] = val.strip();
		
		return data;
	

	def convertMessages(self, requestParam):
		from xml.etree import SimpleXMLTreeBuilder;
		parser = SimpleXMLTreeBuilder.TreeBuilder();
		parser.feed(requestParam);
		xml = parser.close();

		message = {};

		if (xml.find('title') == None or xml.find('content') == None):
			raise Exception, "Mallformed activity xml";
		
		#  remember to either type cast to (string) or trim() the string so we don't get 
		#  SimpleXMLString types in the internal data representation. I often prefer
		#  using trim() since it cleans up the data too
		arrays = {'id': 'id', 'title': 'title', 'body': 'summary'};

		for key in arrays.keys():
			value = arrays[key];
			if(xml.find(value) != None):
				message[key] = xml.find(value).text.strip();
			else:
				message[key] = None;

		#  retrieve recipients by looking at the osapi name space
		xml = simplexml_load_string(requestParam, 'SimpleXMLElement', LIBXML_NOCDATA, "http://opensocial.org/2008/opensocialapi");
		if (xml.find('recipient') == None):
			raise Exception, "Invalid message structure";
		
		message['recipients'] = [];
		for recipient in xml.findall('recipient'):
			message['recipients'].append(recipient.text.strip());
		
		return message;
	



# Convert Xml representations to the internal data structure representation

class InputXmlConverter(InputConverter):

	def convertPeople(self, requestParam):
		raise Exception, "Operation not supported";
	

	def convertActivities(self, requestParam):
		activity = {};
		from xml.etree import SimpleXMLTreeBuilder;
		parser = SimpleXMLTreeBuilder.TreeBuilder();
		parser.feed(requestParam);
		xml = parser.close();

		if (xml.find('title') == None):
			raise Exception, "Mallformed activity xml";
		
		#  remember to either type cast to (string) or trim() the string so we don't get 
		#  SimpleXMLString types in the internal data representation. I often prefer
		#  using trim() since it cleans up the data too
		arrays = {'id': 'id', 'title': 'title', 'body': 'summary', 'updated': 'updated',
			'streamTitle': 'content/activity/streamTitle', 'streamId': 'content/activity/streamId'	
		
			};

		for key in arrays.keys():
			value = arrays[key];
			if(xml.find(value) != None):
				activity[key] = xml.find(value).text.strip();
			else:
				activity[key] = '';

		if (xml.findall('content/activity/mediaItems') != None):
			activity['mediaItems'] = {};
			for mediaItem in xml.findall('content/activity/mediaItems/MediaItem'):
				item = {};
				if (mediaItem.find('type') == None or mediaItem.find('mimeType') == None or mediaItem.find('url')):
					raise Exception, "Invalid media item in activity xml";
				
				item['type'] = xml.find('type').text.strip();
				item['mimeType'] = xml.find('mimeType').text.strip();
				item['url'] = xml.find('url').text.strip();
				activity['mediaItems'].append(item);
			
		return activity;


	def convertAppData(self, requestParam):
		from xml.etree import SimpleXMLTreeBuilder;
		parser = SimpleXMLTreeBuilder.TreeBuilder();
		parser.feed(requestParam);
		xml = parser.close();

		if (xml.find('appdata') == None):
			raise Exception, "Mallformed AppData xml";
		
		data = {};
		
		#from lib import Logger;
		#Logger.quickLog("<b>AppData code need to be fixed</b>", Logger.INFO);

		#foreach (get_object_vars(xml.appdata) as key => val) 
		for key in xml.findall('appdata'):
			val = '';#key[];
			data[key.strip()] = val.strip();
		
		return data;
	

	def convertMessages(self, requestParam):
		from xml.etree import SimpleXMLTreeBuilder;
		parser = SimpleXMLTreeBuilder.TreeBuilder();
		parser.feed(requestParam);
		xml = parser.close();

		message = {};
		if (xml.find('title') == None or xml.find('content') == None):
			raise Exception, "Mallformed activity xml";
		
		#  remember to either type cast to (string) or trim() the string so we don't get 
		#  SimpleXMLString types in the internal data representation. I often prefer
		#  using trim() since it cleans up the data too
		arrays = {'id': 'id', 'title': 'title', 'body': 'summary'};

		for key in arrays.keys():
			value = arrays[key];
			if(xml.find(value) != None):
				message[key] = xml.find(value).text.strip();
			else:
				message[key] = None;

		#  retrieve recipients by looking at the osapi name space
		xml = simplexml_load_string(requestParam, 'SimpleXMLElement', LIBXML_NOCDATA, "http://opensocial.org/2008/opensocialapi");
		if (xml.find('recipient') == None):
			raise Exception, "Invalid message structure";
		
		message['recipients'] = [];
		for recipient in xml.findall('recipient'):
			message['recipients'].append(recipient.text.strip());
		
		return message;

